//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.TridentModelSL
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using service = Microsoft.Research.ScientificWorkflow.TridentSilverlightService;

    /// <summary>
    /// A model describing the meta-data associated with a workflow parameter
    /// of a workflow activity. This data includes whether it is an input or
    /// output paramter, as well as the name, label, and description attached
    /// to that parameter.
    /// </summary>
    public class ParameterDescriptionModel : INotifyPropertyChanged
    {
        #region private variables

        /// <summary>
        /// The Unique id for the parameter.
        /// </summary>
        private Guid id = Guid.Empty;

        /// <summary>
        /// Name of the property as in the custom attribute.
        /// </summary>
        private string activityName;

        /// <summary>
        /// Name of the property as in the class.
        /// </summary>
        private string propertyName;

        /// <summary>
        ///  Description of the parameter.
        /// </summary>
        private string description;

        /// <summary>
        /// Datatype of the parameter.
        /// </summary>
        private string dataType;

        /// <summary>
        /// Value of the parameter.
        /// </summary>
        private string value;

        /// <summary>
        /// A flag which specifies if the parameter is mandatory for the workflow. 
        /// </summary>
        private bool isMandatoryForWorkflow;

        /// <summary>
        /// A flag which specifies whether the parameter is databound to another parameter.
        /// </summary>
        private bool isDatabound;

        /// <summary>
        /// Label of the description model
        /// </summary>
        private string label;

        /// <summary>
        /// A flag which indicates whether the value is valid.
        /// </summary>
        private bool? isValueInvalid;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ParameterDescriptionModel"/> class.
        /// </summary>
        /// <param name="paramModel">The param model.</param>
        public ParameterDescriptionModel(service.ParameterDescriptionModel parameterModel)
        {
            this.activityName = parameterModel.activityName;
            this.ActivityUniqueId = parameterModel.ActivityUniqueIdk__BackingField;
            this.AutomationId = parameterModel.AutomationIdk__BackingField;
            this.dataType = parameterModel.dataType;
            this.description = parameterModel.description;
            this.id = parameterModel.id;
            this.isDatabound = parameterModel.isDatabound;
            this.isMandatoryForWorkflow = parameterModel.isMandatoryForWorkflow;
            this.isValueInvalid = parameterModel.isValueInvalid;
            this.label = parameterModel.label;
            this.propertyName = parameterModel.propertyName;
            this.value = parameterModel.value;
        }

        /// <summary>
        /// Convert to network model.
        /// </summary>
        /// <returns></returns>
        public service.ParameterDescriptionModel ToNetwork()
        {
            service.ParameterDescriptionModel parameter = new service.ParameterDescriptionModel();

            // Set the property Name of the Parameter.
            parameter.propertyName = this.propertyName;

            // Set the Name of the Parameter.
            parameter.activityName = this.activityName;

            parameter.ActivityUniqueIdk__BackingField = this.ActivityUniqueId;

            // Set the user friendly Name of the Parameter.
            parameter.label = this.label;

            // Set the description of the Parameter.
            parameter.description = this.description;

            parameter.isDatabound = this.isDatabound;
            parameter.value = this.Value;

            parameter.isMandatoryForWorkflow = this.isMandatoryForWorkflow;

            // Set datatype.
            parameter.dataType = this.dataType;

            return parameter;
        }

        #endregion

        #region public properties

        /// <summary>
        /// The Unique id for the parameter.
        /// </summary>
        public Guid Id
        {
            get
            {
                return id;
            }
        }

        /// <summary>
        /// Name of the parameter as in the class.
        /// </summary>
        public string PropertyName
        {
            get
            {
                return propertyName;
            }
        }

        /// <summary>
        /// Name of the parameter as in attribute.
        /// </summary>
        public string ActivityName
        {
            get
            {
                return this.activityName;
            }
        }

        /// <summary>
        /// Name of the parameter.
        /// </summary>
        public string Label
        {
            get
            {
                return this.label;
            }
        }

        /// <summary>
        /// Description of the parameter.
        /// </summary>
        public string Description
        {
            get
            {
                return description;
            }
        }

        /// <summary>
        /// Datatype of the parameter.
        /// </summary>
        public string DataType
        {
            get
            {
                return dataType;
            }
        }

        /// <summary>
        /// Value of the parameter.
        /// </summary>
        public string Value
        {
            get
            {
                return value;
            }
            set
            {
                if (value != this.value)
                {
                    this.value = value;
                    this.OnPropertyChanged("Value");
                }
            }
        }

        /// <summary>
        /// Gets or Sets a flag which specifies if the parameter is mandatory for the workflow execution. 
        /// </summary>
        public bool IsMandatoryForWorkflow
        {
            get
            {
                return this.isMandatoryForWorkflow;
            }
            set
            {
                this.isMandatoryForWorkflow = value;
                this.OnPropertyChanged("IsMandatoryForWorkflow");
                this.OnPropertyChanged("IsNotMandatoryForWorkflow");
            }
        }

        /// <summary>
        /// A flag which signifies whether the parameter is mandatory for the workflow execution.
        /// </summary>
        public bool IsNotMandatoryForWorkflow
        {
            get
            {
                return !this.isMandatoryForWorkflow;
            }
        }

        /// <summary>
        /// A flag which specifies whether the parameter is databound to another parameter.
        /// </summary>
        public bool IsDataBound
        {
            get
            {
                return isDatabound;
            }
            set
            {
                if (value != this.isDatabound)
                {
                    this.isDatabound = value;
                    this.OnPropertyChanged("IsDatabound");
                }
            }
        }

        /// <summary>
        /// Gets or sets the automation id.
        /// </summary>
        /// <value>The automation id.</value>
        public string AutomationId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag which indicates whether the value is valid. To be accessed only after it is loaded in the property pane.
        /// Will not hold a proper value before loading.
        /// </summary>
        public bool? IsValueInvalid
        {
            get
            {
                return this.isValueInvalid;
            }
            set
            {
                this.isValueInvalid = value;
                this.OnPropertyChanged("IsValueInvalid");
            }
        }

        /// <summary>
        /// Gets or Sets the name of the activity in which this activity is present. Used only for naming in the property list.
        /// </summary>
        public string PropertyPaneDisplayName
        {
            get
            {
                return string.Format(CultureInfo.CurrentCulture, "{0}.{1}", this.activityName, this.propertyName);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string ActivityUniqueId { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// Validate the entered value against the requirements of the paramModel.
        /// </summary>
        /// <param name="enteredVal">
        /// The current value.
        /// </param>
        /// <param name="paramDesc">
        /// The parameter model for which the value has been specified.
        /// </param>
        /// <returns>
        /// Returns true if valid value, false otherwise.
        /// </returns>
        public static bool ValidateInputValue(string enteredValue, ParameterDescriptionModel parameterDescription)
        {
            bool validValue = true;
            if (parameterDescription != null)
            {
                if (string.IsNullOrEmpty(enteredValue))
                {
                    return false;
                }
                else
                {
                    try
                    {
                        System.Type dataType = System.Type.GetType(parameterDescription.DataType);
                        if (dataType != null)
                        {
                            // System.ComponentModel.TypeConverter converter = new TypeConverter();
                            object parsedValue = System.Convert.ChangeType(enteredValue, dataType, CultureInfo.CurrentCulture);
                            // object parsedValue = converter.ConvertTo(enteredVal, dataType);
                            if (null == parsedValue)
                            {
                                validValue = false;
                            }
                        }
                    }
                    catch (NotSupportedException)
                    {
                        validValue = false;
                    }
                    catch (FormatException)
                    {
                        validValue = false;
                    }
                    catch (Exception)
                    {
                        validValue = false;
                    }
                }
            }

            return validValue;
        }

        /// <summary>
        /// The string representation of the object. Return the display name. Used in automation.
        /// </summary>
        /// <returns>The string representation of the object.</returns>
        public override string ToString()
        {
            if (!string.IsNullOrEmpty(this.PropertyPaneDisplayName))
            {
                return this.PropertyPaneDisplayName;
            }
            return base.ToString();
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Fired on property changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fired on property changed.
        /// </summary>
        /// <param name="changedProperty">
        /// changedProperty.
        /// </param>
        private void OnPropertyChanged(string changedProperty)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(changedProperty));
            }
        }

        #endregion
    }
}
